using CsGL.OpenGL;
using CsGL.Util;
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
using System.Reflection;

namespace TagTool.OpenGL
{

	public sealed class App 
	{

		// Application Objects

		private Form form;
		private View view;
		private Model model;
		
		// Application State
		private bool isDisposed = false;											// Has Dispose Been Called?
		private bool isFullscreen = false;										// Is Application In Fullscreen Or Windowed Mode?
		private bool isRunning = true;											// Is Application Currently Running?
		private bool isActive = true;											// Is The Application Active?

		private bool runWhileMinimized = true;									// Continue Running The Application's Main Loop Even When Application Is Minimized?
		
		private bool showCursorFullscreen = false;								// Show Mouse Cursor While Application Is Fullscreen?
		private bool showCursorWindowed = true;									// Show Mouse Cursor While Application Is Windowed?

		// OpenGL State
		private int width = 1024;													// The Current Width Of The Application.
		private int height = 768;												// The Current Height Of The Application.
		private byte colorDepth = 16;											// The Current Color Depth, In Bits Per Pixel.

		private bool isRgba = true;												// RGBA Or Color Index Mode?

		private byte accumDepth = 0;												// OpenGL's Accumulation Buffer Depth, In Bits Per Pixel.
		private byte stencilDepth = 0;											// OpenGL's Stencil Buffer Depth, In Bits Per Pixel.
		private byte zDepth = 16;												// OpenGL's Z-Buffer Depth, In Bits Per Pixel.
		
		private double nearClippingPlane = 0.1f;									// GLU's Distance From The Viewer To The Near Clipping Plane (Always Positive).
		private double farClippingPlane = 10000.0f;								// GLU's Distance From The Viewer To The Far Clipping Plane (Always Positive).
		private double fovY = 45.0f;		

		public App(Model model)
		{
			this.model = model;
			CreateForm();
		
		}
		
		#region Public Properties
		public byte AccumDepth {
			get {
				return accumDepth;
			}
			set {
				accumDepth = value;
			}
		}

		/// <summary>
		/// Gets or sets the current color depth, in bits per pixel.
		/// </summary>
		public byte ColorDepth {
			get {
				return colorDepth;
			}
			set {
				colorDepth = value;
			}
		}


		/// <summary>
		/// Gets or sets GLU's distance from the viewer to the far clipping plane (always positive).
		/// </summary>
		public double FarClippingPlane {
			get {
				return farClippingPlane;
			}
			set {
				farClippingPlane = value;
			}
		}

		/// <summary>
		/// Gets or sets the application's form.  Should likely only be used in <see cref="CsGL.Basecode.Model.WindowsForm" />.
		/// </summary>
		public Form Form {
			get {
				return form;
			}
			set {
				form = value;
			}
		}

		/// <summary>
		/// Gets or sets GLU's field of view angle, in degrees, in the Y direction.
		/// </summary>
		public double FovY {
			get {
				return fovY;
			}
			set {
				fovY = value;
			}
		}


		/// <summary>
		/// Gets or sets the height of the application's window at creation.
		/// </summary>
		public int Height {
			get {
				return height;
			}
			set {
				height = value;
			}
		}

		/// <summary>
		/// Gets or sets the application's active state.  You could use this to make your application 
		/// idle while it's inactive by setting this property in your Windows Form's Activate and 
		/// Deactivated events.
		/// </summary>
		public bool IsActive {
			get {
				return isActive;
			}
			set {
				isActive = value;
			}
		}

		/// <summary>
		/// Gets or sets whether the application is currently in fullscreen or windowed mode.  You
		/// should likely only use this to get, except if you define it in <see cref="Model.Setup" />.
		/// After the start of your application, use <see cref="App.ToggleFullscreen" />.
		/// </summary>
		public bool IsFullscreen {
			get {
				return isFullscreen;
			}
			set {
				isFullscreen = value;
			}
		}

		/// <summary>
		/// Gets or sets whether the application uses RGBA or color index mode.
		/// </summary>
		public bool IsRgba {
			get {
				return isRgba;
			}
			set {
				isRgba = value;
			}
		}

		/// <summary>
		/// Gets whether or not the application is currently running.
		/// </summary>
		public bool IsRunning {
			get 
			{
				return isRunning;
			}
		}

		/// <summary>
		/// Gets the application's <see cref="Model" />.
		/// </summary>
		public Model Model {
			get {
				return model;
			}
		}

		/// <summary>
		/// Gets or sets GLU's distance from the viewer to the near clipping plane (always positive).
		/// </summary>
		public double NearClippingPlane {
			get {
				return nearClippingPlane;
			}
			set {
				nearClippingPlane = value;
			}
		}

		/// <summary>
		/// Gets or sets whether the application should continue running the main loop even when 
		/// the application is minimized.  If it shouldn't, it'll wait until the application is put 
		/// back into normal mode, saving CPU cycles.
		/// </summary>
		public bool RunWhileMinimized {
			get {
				return runWhileMinimized;
			}
			set {
				runWhileMinimized = value;
			}
		}

		/// <summary>
		/// Gets or sets whether the mouse cursor should be displayed in fullscreen mode.
		/// </summary>
		public bool ShowCursorFullscreen {
			get {
				return showCursorFullscreen;
			}
			set {
				showCursorFullscreen = value;
			}
		}

		/// <summary>
		/// Gets or sets whether the mouse cursor should be displayed in windowed mode.
		/// </summary>
		public bool ShowCursorWindowed {
			get {
				return showCursorWindowed;
			}
			set {
				showCursorWindowed = value;
			}
		}

		/// <summary>
		/// Gets or sets OpenGL's stencil buffer depth, in bits per pixel.
		/// </summary>
		public byte StencilDepth {
			get {
				return stencilDepth;
			}
			set {
				stencilDepth = value;
			}
		}

		/// <summary>
		/// Gets the application's current <see cref="View" />.
		/// </summary>
		public View View {
			get {
				return view;
			}
		}

		/// <summary>
		/// Gets or sets the width of the application's window at creation.
		/// </summary>
		public int Width {
			get {
				return width;
			}
			set {
				width = value;
			}
		}

		/// <summary>
		/// Gets or sets OpenGL's Z-buffer depth, in bits per pixel.
		/// </summary>
		public byte ZDepth {
			get {
				return zDepth;
			}
		}
		#endregion Public Properties

	

		#region Dispose()
		private void Dispose() {
			Dispose(true);																// We've Manually Called For A Dispose
		}
		#endregion Dispose()

		#region Dispose(bool disposing)
		private void Dispose(bool disposing) {
			if(!isDisposed) {															// Check To See If Dispose Has Already Been Called
				if(disposing) {															// If disposing Equals true, Dispose All Managed And Unmanaged Resources
					if(form != null) {
						form.Close();
					}
					if(view != null) {
						view.Dispose();
					}
					if(model != null) {
						model.Dispose();
					}
				}

				// Release Any Unmanaged Resources Here, If disposing Was false, Only The Following Code Is Executed
				isRunning = false;
				form = null;
				view = null;
				model = null;
			}
			isDisposed = true;															// Mark As disposed
		}
		#endregion Dispose(bool disposing)

		#region Finalizer
		~App() {
			Dispose(false);																// We've Automatically Called For A Dispose
		}
		#endregion Finalizer


		private void CreateForm() {
			try {
				if(model != null) {
					view = new View(model, this);												// Create A New OpenGL View
					view.Context.Grab();												// Grab The OpenGL Context
					model.Initialize();													// Run Model's Initialize()
					OpenGLException.Assert();											// Check For Any OpenGL Errors

					if(isFullscreen) {											// If We're In Fullscreen Mode
						//form = new ScreenForm(width, height);
						form = new ScreenForm(1024, 768);
						this.View.Dock = DockStyle.Fill;									// Fill It With view
						form.Controls.AddRange(new Control[] { view });					// Add view
						form.Show();													// Show The Fullscreen Form
					}
					else {																// Otherwise
						model.WindowsForm(this);											// Create The User's Defined Windows Form
					}
				}
				if(isFullscreen) {														// If We're In Fullscreen Mode
					if(showCursorFullscreen) {											// If We're Supposed To Show Cursor
						Cursor.Show();													// Show It
					}
					else {																// Otherwise
						Cursor.Hide();													// Hide It
					}
				}
				else {																	// Else We're In Windowed Mode
					if(showCursorWindowed) {											// If We're Supposed To Show Cursor
						Cursor.Show();													// Show It
					}
					else {																// Otherwise
						Cursor.Hide();													// Hide It
					}
				}
			}
			catch(Exception e) {
				// Handle Any Exceptions While Creating The Application's Form, Exit App
				string errorMsg = "A Basecode Error Occurred While Creating The Application's Form:\n\nStack Trace:\n\t" + e.StackTrace + "\n";
				MessageBox.Show(errorMsg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
				this.Terminate();
			}
		}

		private void DestroyForm() {
			if(form != null) {
				form.Hide();															// Hide The Form
				form.Close();															// Close And Dispose Of The Form
				form = null;
			}
			if(view != null) {
				view.Dispose();
				view = null;
			}
			GC.Collect();
		}

	
		public void Terminate() {
			isRunning = false;															// Stop The Application On The Next Loop
		}

		public void ToggleFullscreen() {
			DestroyForm();																// Get Rid Of The Current Form
			isFullscreen = !isFullscreen;												// Toggle Our Fullscreen State
			CreateForm();																// Create A New Form															// Reset Our Framerate
			form.Activate();
			form.BringToFront();
			
			if(isFullscreen) {															// If We're In Fullscreen Mode
				if(showCursorFullscreen) {												// If We're Supposed To Show Cursor
					Cursor.Show();														// Show It
				}
				else {																	// Otherwise
					Cursor.Hide();														// Hide It
				}
			}
			else {																		// Else We're In Windowed Mode
				if(showCursorWindowed) {												// If We're Supposed To Show Cursor
					Cursor.Show();														// Show It
				}
				else {																	// Otherwise
					Cursor.Hide();														// Hide It
				}
			}
			//MainLoop();																	// Return To The Main Loop
		}

		[DllImport("user32")]
		private static extern void WaitMessage();

	}
}
